import { Injectable } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import { isoToYYYYMMDDHHmmss, md5, timeToISO } from 'src/utils';
import { CreateUserDto } from './dto/create-user.dto';
import { FindAllUserDto } from './dto/find-all-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

interface QueryWhere {
  status?: number;
  OR?: { [key: string]: { contains: string } }[];
  AND?: { [key: string]: { gte: string } | { lte: string } }[];
  deptId?: number;
}

@Injectable()
export class UserService {
  constructor(private prisma: PrismaService) {}

  /**
   * 创建一个新用户以及相关联的角色记录。
   * @param createUserDto 包含用户创建所需信息的数据传输对象，包括角色ID。   *
   * @returns 返回创建的用户对象及其关联的角色。
   */
  create(createUserDto: CreateUserDto) {
    const { roleIds, ...userDto } = createUserDto;
    const rolesArr = roleIds?.map((roleId) => ({ roleId }));

    return this.prisma.user.create({
      data: {
        ...userDto,
        roles: {
          create: rolesArr,
        },
      },
    });
  }

  /**
   * 查找所有用户的信息。
   * @param query 包含查找条件的对象，可指定状态、关键词、时间范围等。
   * @returns 返回一个对象，包含用户列表和总数。
   */
  async findAll(query: FindAllUserDto) {
    try {
      // 构建查询条件
      const queryWhere = this.buildQueryWhere(query);

      // 使用Prisma客户端进行事务查询，同时获取用户列表和总数
      const [users, count, depts, genderDicts] = await Promise.all([
        this.prisma.user.findMany({
          where: queryWhere,
          orderBy: {
            id: 'desc',
          },
          skip: (query.pageNum - 1) * query.pageSize,
          take: query.pageSize,
          select: {
            id: true,
            username: true,
            nickname: true,
            mobile: true,
            status: true,
            avatar: true,
            email: true,
            createTime: true,
            deptId: true,
            roles: {
              select: {
                role: {
                  select: {
                    name: true,
                  },
                },
              },
            },
            gender: true,
          },
        }),
        this.prisma.user.count({
          where: queryWhere,
        }),
        this.prisma.dept.findMany(),
        this.prisma.dict.findMany({ where: { typeCode: 'gender' } }),
      ]);

      // 处理用户列表
      const userList = users.map((user) => {
        const dept = depts.find((dept) => dept.id === user.deptId);
        const genderDict = genderDicts.find((dict) => dict.value === String(user.gender));

        let roleNames: string;
        if (user.roles) {
          roleNames = user.roles.map((item) => item.role.name).join('');
          delete user.roles;
        }

        return {
          ...user,
          deptName: dept?.name,
          roleNames,
          genderLabel: genderDict?.name,
          createTime: isoToYYYYMMDDHHmmss(user.createTime).split(' ')[0],
        };
      });

      return {
        list: userList,
        total: count,
      };
    } catch (error) {
      throw new Error(`Error finding users: ${error.message}`);
    }
  }

  /**
   * 根据查询条件构建查询的where对象。
   * @param query 包含查找条件的对象。
   * @returns 返回一个构建好的查询where对象。
   */
  buildQueryWhere(query: FindAllUserDto): QueryWhere {
    const queryWhere: QueryWhere = {};

    // 构建查询条件
    if (query.status !== undefined && query.status !== null) {
      queryWhere.status = query.status;
    }

    if (query.keywords && query.keywords.trim() !== '') {
      // 如果有关键词，则在用户名、昵称、手机号中匹配
      queryWhere.OR = [{ username: { contains: query.keywords } }, { nickname: { contains: query.keywords } }, { mobile: { contains: query.keywords } }];
    }

    if (query.startTime && query.endTime) {
      // 如果指定了开始时间和结束时间，则构建时间范围条件
      queryWhere.AND = [{ createTime: { gte: timeToISO(query.startTime) } }, { createTime: { lte: timeToISO(query.endTime) } }];
    }

    if (query.deptId) {
      // 如果指定了部门ID，则添加部门ID条件
      queryWhere.deptId = query.deptId;
    }

    return queryWhere;
  }

  /**
   * 根据用户ID、角色和菜单查询用户信息
   * @param id 用户ID
   * @returns 返回包含用户头像、昵称、用户名、角色代码数组、权限代码数组和用户ID的对象
   */
  async findOneByRoleAndMenu(id: number) {
    // 通过Prisma客户端查询唯一的用户信息，选择特定的字段
    const {
      avatar,
      nickname,
      id: userId,
      username,
      roles,
    } = await this.prisma.user.findUnique({
      where: { id },
      select: {
        avatar: true,
        nickname: true,
        id: true,
        username: true,
        roles: {
          select: {
            role: {
              select: {
                code: true,
                menus: {
                  select: {
                    menu: {
                      select: {
                        perm: true,
                      },
                    },
                  },
                },
              },
            },
          },
        },
      },
    });

    // 提取用户角色代码数组
    const rolesCodes = roles.map((item) => item.role.code);
    // 提取角色菜单权限数组
    const menuPerms = roles.flatMap((item) => item.role.menus.flatMap((item2) => (item2.menu.perm ? item2.menu.perm : [])));

    // 返回用户信息，包括角色代码和权限代码
    return {
      avatar,
      nickname,
      username,
      roles: rolesCodes,
      perms: menuPerms,
      userId,
    };
  }
  /**
   * 更新用户信息
   * @param id 用户的唯一标识符
   * @param updateUserDto 更新用户信息的数据传输对象，包含需要更新的用户信息
   * @returns 返回更新后的用户信息数据传输对象
   */

  async update(id: number, updateUserDto: UpdateUserDto) {
    const { roleIds, ...userDto } = updateUserDto;
    const roles = roleIds?.map((roleId) => ({ roleId }));
    const rolesData = {
      create: roles,
    };

    await this.prisma.$transaction(async (prisma) => {
      // 删除用户原有的所有角色关联
      await prisma.usersOnRoles.deleteMany({
        where: {
          userId: id,
        },
      });

      // 更新用户信息，并重新建立新的角色关联
      await prisma.user.update({
        where: { id },
        data: {
          ...userDto,
          roles: {
            create: rolesData.create,
          },
        },
      });
    });
  }

  /**
   * 此 TypeScript 函数使用事务来确保同时删除用户角色关联和用户本身。
   * @param {number} id - 您提供的 `remove` 函数是一个异步函数，它使用事务来确保删除用户的角色和删除用户本身都在同一操作中执行。
   * @returns `remove` 函数返回一个 Promise，该 Promise 解析为事务内执行的两个删除操作的结果数组。该数组将包含从 `usersOnRoles`
   * 表中删除用户角色的结果以及从 `user` 表中删除用户的结果。
   */
  remove(id: number) {
    // 使用事务来确保在一个操作中同时执行删除用户和删除角色中间表对应数据的操作
    return this.prisma.$transaction([
      // 删除用户关联的角色中间表对应数据
      this.prisma.usersOnRoles.deleteMany({
        where: {
          userId: id,
        },
      }),
      // 删除用户
      this.prisma.user.delete({
        where: {
          id: id,
        },
      }),
    ]);
  }

  /**
   * 根据用户名查找用户。
   *
   * @param username 用户名。
   * @returns 返回找到的第一个用户记录，如果没有找到则返回null。
   */
  findByUsername(username: string) {
    // 使用Prisma客户端查询数据库，查找第一个匹配给定用户名的用户
    return this.prisma.user.findFirst({ where: { username } });
  }

  /**
   * 更新用户密码
   * @param id 用户ID
   * @param password 新密码
   * @returns 返回更新后的用户数据
   */
  updatePassword(id: number, password: string) {
    // 使用md5对新密码进行加密后更新数据库中的用户密码
    return this.prisma.user.update({
      where: { id },
      data: {
        password: md5(password),
      },
    });
  }

  /**
   * 根据ID查找用户表单信息
   * @param id 用户ID
   * @returns 返回包含用户详细信息的对象，如ID、用户名、昵称、部门ID、性别、角色、手机号、电子邮箱和状态等
   */
  async findForm(id: number) {
    // 查询指定ID的用户信息，选择性返回包括ID、用户名、昵称、部门ID、性别、角色、手机号、电子邮箱和状态在内的字段
    const result = await this.prisma.user.findUnique({
      where: { id },
      select: {
        id: true,
        username: true,
        nickname: true,
        deptId: true,
        gender: true,
        mobile: true,
        email: true,
        status: true,
        roles: {
          select: {
            roleId: true,
          },
        },
      },
    });

    // 提取用户角色代码数组
    const roleIds = result.roles.map((item) => item.roleId);
    delete result.roles;
    return { ...result, roleIds };
  }
}
